﻿/**
 * 操作历史模块
 * IE浏览器用数组记录编辑器的没一步，非IE浏览器使用系统命令
 */
(function(E){
var Consts = E.consts,

	// 记录初始化的内容
	initContent={},
	Event = E.mod('Event');

E.mod("History", {
	Redo: function( ){
		History.redo( );
	},
	
	Undo: function( ){
		History.undo( );
	},
	
	ischange : function( ){
		return History.stack[E.eid].length;
	},
	
	// 重置历史记录
	// 当内容发生变化之后需要重新开始历史记录时，特别有用
	reset: function(){
		var Undo = E.plugin('Undo'),
			Redo = E.plugin('Redo');
		
		Undo.disable();
		Redo.disable();
		reset({value:E.get('option'), html:E.get('document').body.innerHTML});
	}
	
});

function reset(e){
	// 记录初始化内容，使用 setTimeout 延迟执行
	// 以保证一些对内容进行的转换操作在此之前执行
	// 防止在第一次执行撤销操作时，使内容回退到内容转换之前的状态
	setTimeout(function(){
		var option = e.value, eid = option.id, html = e.html;
		History.stack[eid] = [];
		History.bookmarks[eid] = [];
		
		//记录初始化的内容
		initContent[eid] = html;
		
		//历史记录指针从-1开始
		History.pointer[eid] = -1;
	}, 1);
}

Event.bind('onmonitor', function onmonitor_history( e ){
	if( History.ischange() ){
		// 在内容发生变化时，添加历史记录
		History.add();
	}
});

Event.bind('click', function oncreate_history( e ){
	History.saveBookmark();
});

Event.bind('oncomplete', function oncomplete_history( e ){
	reset(e);
});

var History = {
	// 历史记录，二维数组，一维键值是eid，二维数组从 0 开始
	stack:[],
	
	
	pointer:[],
	
	// 记录最后修改的数据内容，以判断是否禁用 "重做" 按钮
	last:[],
	
	// 尝试记录历史版本光标的位置
	bookmarks:{},
	
	ischange: function( ){
		var self = this,
			eid = E.eid,
			dom = E.get('document');
		
		return (dom.body.innerHTML !== self.last[eid]);
	},
	
	// 保存当前光标
	saveBookmark: function(){
		var self = this,
			eid = E.eid,
			Selection = E.mod('Selection'),
			p = self.pointer[eid];
			
		if( Consts.IE ){
			var bookmark = self.bookmarks[eid];
			bookmark[p] = Selection.getBookmark();
		}
	},
	
	// 记录键盘输入,点击插件,粘帖等行为导致的内容变化
	add: function( ){
		var self = this,
			eid = E.eid,
			Confirm = E.mod('Confirm'),
			Selection = E.mod('Selection'),
			p = self.pointer[eid],
			last = self.last,
			dom = E.get('document'),
			html = '',
			Undo = E.plugin('Undo'),
			Redo = E.plugin('Redo'),
			stack = self.stack[eid];
			
		
		// 设置页面离开提示信息，防止意外刷新或关闭导致数据丢失
		if( Confirm ){ Confirm.set(); }
		
		if( Consts.IE ){
			var bookmark = self.bookmarks[eid];
			html = dom.body.innerHTML;
			
			if( stack[p] ){
				var len = Math.abs(html.length - stack[p].length);//数据差异
				if ( len < 5 ){
					//和当前内容的差异太小
					E.log('run', '和当前内容的差异太小，没有增加历史记录');
					return 0;
				}
			}
			
			p += 1;
			bookmark[p] = Selection.getBookmark();
			last[eid] = dom.body.innerHTML;
			//Selection.clearBookmark();
			self.pointer[eid] = p;
			
			stack[p] = last[eid];
		}else{
			last[eid] = '';
			p += 1;
			self.pointer[eid] = p;
			
			stack[p] = last[eid];
		}
		Undo && Undo.enable();
		Redo && Redo.disable();
		
		if( p < stack.length-1 ){
			stack.splice(p+1, stack.length-1-p);
		}
		E.log('run', '增加一条历史记录, pointer='+p);
		return 1;
	},
		
	// 撤销
	undo: function( ){
		var self = this,
			dom = E.get('document'),
			Selection = E.mod('Selection'),
			eid = E.eid,
			stack = self.stack[eid],
			bookmark = self.bookmarks[eid],
			p = --self.pointer[eid],
			html = '',
			Undo = E.plugin('Undo'),
			Redo = E.plugin('Redo');
		
		html = (p>=0)? stack[p] : initContent[eid];
		self.last[eid] = html;
		if( Consts.IE ){
			dom.body.innerHTML = html;
			Selection.moveToBookmark( bookmark[p] );
		}else{
			E.execCommand('Undo');
		}
		Redo && Redo.enable();
		
		if ( p<0 && Undo){ Undo.disable(); }
		E.log('run', 'undo');
		Event.trigger( {type:'onundo'} );
		return 1;
	},
	
	//重做
	redo: function( ){
		var self = this,
			dom = E.get('document'),
			eid = E.eid,
			Selection = E.mod('Selection'),
			stack=self.stack[eid],
			bookmark = self.bookmarks[eid],
			p = ++self.pointer[eid],
			html = '',
			last=self.last,
			Undo = E.plugin('Undo'),
			Redo = E.plugin('Redo');
		
		html = stack[p];
		self.last[eid] = html;
		
		if( Consts.IE ){
			dom.body.innerHTML = html;
			Selection.moveToBookmark( bookmark[p] );
		}else{
			E.execCommand('Redo');
		}
		
		Undo.enable();
		
		if ( p >= stack.length-1 ){
			Redo.disable();
		}
		E.log('run', 'redo');
		Event.trigger( {type:'onredo'} );
		return 1;
	}
}


})(jQEditor);